package com.xiaoma.h5testdemo.business.main.utils

import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import com.xiaoma.common.ui.BaseFragment
import com.xiaoma.lib.log.KLog
import java.util.*

/**
 * author: liangjingbo
 * date: 2022/6/23
 * describe: 简单管理 Fragment 栈的管理类
 * 添加：
 * 为了避免在同一个 containerId 上添加多个 Fragment 导致过度绘制，所以管理类中采取的策略就是
 * 先隐藏当下正在显示 Fragment ，然后在添加准备显示 Fragment（该 Fragment 进入 Stack）
 * 退栈：
 * 如果栈中只有一个 Fragment 或者当前现显示的 Fragment 是首页Fragment，则退栈失败，不允许退栈了；
 * 否则先将最上面那个 Fragment（该 Fragment 退出 Stack） 给 Remove 了，然后显示接下一个 Fragment（该 Fragment 不退出 Stack ）
 */
class XmFragmentStackManager(private val homeFragment: Class<*>, prefixTag: String = "") {

    private val TAG = "$prefixTag${XmFragmentStackManager::class.java.simpleName}Tag"
    private val mFragmentStack: Stack<Class<out BaseFragment<*>>> = Stack()

    fun changeFragment(
        fragmentManager: FragmentManager,
        showFragment: BaseFragment<*>,
        @IdRes containerId: Int,
        tag: String = showFragment.javaClass.simpleName,
        commitNow: Boolean = true
    ) {
        changeFragment(
            fragmentManager,
            showFragment,
            null,
            containerId,
            tag,
            commitNow
        )
    }

    private fun changeFragment(
        fragmentManager: FragmentManager,
        showFragment: BaseFragment<*>,
        removeFragment: BaseFragment<*>? = null,
        @IdRes containerId: Int,
        tag: String = showFragment.javaClass.simpleName,
        commitNow: Boolean = true
    ) {
        KLog.i(
            TAG, "changeFragment() mFragmentStack.size ${mFragmentStack.size} " +
                    "ready To Show Fragment: $showFragment ,removeFragment: $removeFragment"
        )
        fragmentManager.apply {
            val beginTransaction = beginTransaction()
            var hasAdd = true
            val currentFragment = findFragmentByTag(tag)
                ?: showFragment.also { hasAdd = false }
            fragments
                .filter {
                    it.javaClass.simpleName != currentFragment.javaClass.simpleName
                }.forEach {
                    if (it == removeFragment) {
                        beginTransaction.remove(it)
                        (it as? BaseFragment<*>)?.let { f -> mFragmentStack.remove(f.javaClass) }
                    } else {
                        beginTransaction.hide(it)
                        beginTransaction.setMaxLifecycle(it, Lifecycle.State.STARTED)
                    }
                }
            if (!hasAdd) {
                beginTransaction.add(containerId, currentFragment, tag)
                beginTransaction.setMaxLifecycle(currentFragment, Lifecycle.State.RESUMED)
                //暂不支持用同一全类名 Fragment 进栈出栈操作，因为 findFragmentByTag() 是通过 simpleName 来查找的
                if (!mFragmentStack.contains(showFragment.javaClass)) {
                    mFragmentStack.push(showFragment.javaClass)
                }
            } else {
                beginTransaction.show(currentFragment)
                beginTransaction.setMaxLifecycle(currentFragment, Lifecycle.State.RESUMED)
            }
            if (commitNow) {
                beginTransaction.commitNowAllowingStateLoss()
            } else {
                beginTransaction.commitAllowingStateLoss()
            }
        }

    }

    fun popFragment(fragmentManager: FragmentManager, containerId: Int): Boolean {
        KLog.i(TAG, "popFragment() mFragmentStack.size: ${mFragmentStack.size}---")
        if (mFragmentStack.size <= 1) return false
        KLog.i(TAG, "popFragment() showingFragment: ${getShowFragment()}")
        if (getShowFragment() == homeFragment) return false
        val removeFragmentClz = mFragmentStack.pop()
        val showFragmentClz = mFragmentStack.peek()
        val removeFragment = fragmentManager.findFragmentByTag(removeFragmentClz.simpleName) as? BaseFragment<*>
        val showFragment = fragmentManager.findFragmentByTag(showFragmentClz.simpleName) as? BaseFragment<*>
            ?: showFragmentClz.newInstance()
        KLog.i(TAG, "popFragment() removeFragment: $removeFragment showFragment: $showFragment---")
        changeFragment(fragmentManager, showFragment, removeFragment, containerId)
        return true
    }

    fun clearFragmentStack() {
        mFragmentStack.clear()
    }

    fun getShowFragment(): Class<out Fragment> {
        if (mFragmentStack.empty()) {
            return Fragment::class.java
        }
        return mFragmentStack.peek()
    }


}